package com.ack.web;

import java.util.concurrent.locks.ReentrantLock;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.ack.common.ServiceException;
import com.ack.config.RedisPool;
import com.ack.constant.RedisCons;
import com.ack.enums.RespCode;
import com.ack.util.RespUtil;
import com.ack.vo.TicketInitVo;
import com.ack.vo.TicketPurchaseVo;
import com.ack.vo.TicketRemainVo;
import com.alibaba.fastjson.JSONObject;

/**
 * 火车票服务（购票、退票、余票查询、经停站查询、座位list查询）
 * 
 * @author chenzhao @date Sep 24, 2019
 */
@RestController
@RequestMapping("ticket")
public class TickerController {

	private static final Logger log = LoggerFactory.getLogger(TickerController.class);
	
	ReentrantLock lock=new ReentrantLock();

	/**
	 * 往redis初始化列车基本数据(经停站、座位list)
	 * 
	 * @author chenzhao @date Sep 24, 2019
	 * @param locale
	 * @param model
	 * @return
	 */
	@RequestMapping("/traintinit")
	Object traintinit(@RequestBody TicketInitVo ticketInitVo) {
		if (StringUtils.isEmpty(ticketInitVo.getTrainId())) {
			throw new ServiceException(RespCode.PARAM_INCOMPLETE, "TrainId");
		}
		if (StringUtils.isEmpty(ticketInitVo.getStopStation())) {
			throw new ServiceException(RespCode.PARAM_INCOMPLETE, "StopStation");
		}
		if (StringUtils.isEmpty(ticketInitVo.getSeatList())) {
			throw new ServiceException(RespCode.PARAM_ILLEGAL, "SeatList",
					"格式正确示例(最大值)：9x23x99，表示：每排五个座位9一节车厢23排x一列火车99节车厢");
		} else {
			if (!ticketInitVo.getSeatList().matches("^[1-9][x][0-9]{2}[x][0-9]{1,2}$")) {
				throw new ServiceException(RespCode.PARAM_ILLEGAL, "SeatList",
						"格式正确示例(最大值)：9x23x99，表示：每排五个座位9一节车厢23排x一列火车99节车厢");
			}
			if (Integer.parseInt(ticketInitVo.getSeatList().split("x")[1]) > 26) {
				throw new ServiceException(RespCode.PARAM_ILLEGAL, "SeatList", "车厢最多23排");
			}
			if (Integer.parseInt(ticketInitVo.getSeatList().split("x")[2]) == 0) {
				throw new ServiceException(RespCode.PARAM_ILLEGAL, "SeatList", "车厢数不能为0");
			}
		}

		String[] seatGroup = { "A", "B", "C", "D", "E", "F", "G", "H", "Y", "J", "K", "L", "M", "N", "O", "P", "Q", "R",
				"S", "T", "U", "V", "W", "X", "Y", "Z" };
		StringBuffer seatList = new StringBuffer();
		int ilenth = Integer.parseInt(ticketInitVo.getSeatList().split("x")[2]);
		int jlenth = Integer.parseInt(ticketInitVo.getSeatList().split("x")[1]);
		int klenth = Integer.parseInt(ticketInitVo.getSeatList().split("x")[0]);
		for (int i = 1; i < ilenth + 1; i++) {
			for (int j = 0; j < jlenth; j++) {
				for (int k = 1; k < klenth + 1; k++) {
					seatList.append(i).append("-").append(seatGroup[j]).append(k).append(",");
				}
			}
		}
		ticketInitVo.setSeatList(seatList.substring(0, seatList.lastIndexOf(",")));
		RedisPool.set(RedisCons.trainInfoKey + ticketInitVo.getTrainId(), JSONObject.toJSON(ticketInitVo));
		log.info("redis.ticketInitVo:[{}]", RedisPool.get(RedisCons.trainInfoKey + ticketInitVo.getTrainId()));
		// 移除列车所有购票信息(购票历史订单，持久化在订单表)
		String[] seatListArray = ticketInitVo.getSeatList().split(",");
		for (String seat : seatListArray) {
			String seatKey = RedisCons.ticketPurchaseList + ticketInitVo.getTrainId() + "-" + seat;
			RedisPool.del(seatKey);
		}
		return RespUtil.baseResp(true);
	}

	@RequestMapping("/purchases")
	Object purchases(@RequestBody TicketPurchaseVo ticketPurchaseVo) {
		if (StringUtils.isEmpty(ticketPurchaseVo.getTrainId())) {
			throw new ServiceException(RespCode.PARAM_INCOMPLETE, "TrainId");
		}
		if (StringUtils.isEmpty(ticketPurchaseVo.getStartStation())) {
			throw new ServiceException(RespCode.PARAM_INCOMPLETE, "StartStation");
		}
		if (StringUtils.isEmpty(ticketPurchaseVo.getEndStation())) {
			throw new ServiceException(RespCode.PARAM_INCOMPLETE, "EndStation");
		}
		if (StringUtils.isEmpty(ticketPurchaseVo.getPurchaseCount())) {
			throw new ServiceException(RespCode.PARAM_INCOMPLETE, "PurchaseCount");
		}
		// 不用查询是否有余票，前端一般已经调了余票查询接口，所以直接购票即可，竞争失败再提示失败
		if (ticketPurchaseVo.getPurchaseCount() > 1) {
			// 多人购票（需要优先连坐，再看是否优先靠窗）连坐抢票算法TODO

		} else {
			// 单人购票（无需考虑连坐）
			// 获取列车座位list信息
			String ticketInfoStr = RedisPool.get(RedisCons.trainInfoKey + ticketPurchaseVo.getTrainId());
			String seatListStr = JSONObject.parseObject(ticketInfoStr).get("seatList") + "";
			String stopStationStr = JSONObject.parseObject(ticketInfoStr).get("stopStation") + "";
			// 获取用户购票经停站
			String userStopStations = "";
			try {
				userStopStations = stopStationStr.substring(stopStationStr.indexOf(ticketPurchaseVo.getStartStation()),
						stopStationStr.indexOf(ticketPurchaseVo.getEndStation())) + ticketPurchaseVo.getEndStation();
			} catch (Exception e) {
				throw new ServiceException(RespCode.ticket_voyageError);
			}
			String[] seatListArray = seatListStr.split(",");
			for (String seat : seatListArray) {
				String seatKey = RedisCons.ticketPurchaseList + ticketPurchaseVo.getTrainId() + "-" + seat;
				synchronized (TickerController.class) {// 同步块，只能解决单节点部署下的并发问题，如果是服务多节点部署，必须要用分布式锁ex：redlock
					try {
//						lock.lock();
						String oldSeatStations = RedisPool.get(seatKey);
						if (!StringUtils.isEmpty(oldSeatStations)) {
							if (!oldSeatStations.contains(ticketPurchaseVo.getStartStation())
									&& !oldSeatStations.contains(ticketPurchaseVo.getEndStation())) {
								RedisPool.set(seatKey, oldSeatStations + "," + userStopStations);
								log.info("#购票成功-复坐,#oldSeatStations:[{}],#userStopStations:[{}]", oldSeatStations,
										userStopStations);
								// 和核心抢票、退票、无关的，统一异步完成（异步记录购票人信息，以及对应订单信息）TODO
								return RespUtil.dataResp(seat);
							}
						} else {
							RedisPool.set(seatKey, userStopStations);
							log.info("#购票成功-新坐,#userStopStations:[{}]", userStopStations);
							return RespUtil.dataResp(seat);
						}
					} catch (Exception e) {
						e.printStackTrace();
					}finally {
//						lock.unlock();
					}
				}
			}
		}
		throw new ServiceException(RespCode.ticket_noSeat);
	}

	@RequestMapping("/refund")
	Object refund(@RequestBody TicketPurchaseVo ticketPurchaseVo) {
		if (StringUtils.isEmpty(ticketPurchaseVo.getTrainId())) {
			throw new ServiceException(RespCode.PARAM_INCOMPLETE, "TrainId");
		}
		if (StringUtils.isEmpty(ticketPurchaseVo.getPurchaseSeat())) {
			throw new ServiceException(RespCode.PARAM_INCOMPLETE, "PurchaseSeat");
		}
		if (StringUtils.isEmpty(ticketPurchaseVo.getStartStation())) {
			throw new ServiceException(RespCode.PARAM_INCOMPLETE, "StartStation");
		}
		if (StringUtils.isEmpty(ticketPurchaseVo.getEndStation())) {
			throw new ServiceException(RespCode.PARAM_INCOMPLETE, "EndStation");
		}
		String seatKey = RedisCons.ticketPurchaseList + ticketPurchaseVo.getTrainId() + "-"
				+ ticketPurchaseVo.getPurchaseSeat();
		String oldSeatStations = RedisPool.get(seatKey);
		if (StringUtils.isEmpty(oldSeatStations)) {
			throw new ServiceException(RespCode.ticket_repeatRefund);
		}
		log.info("#oldSeatStations:[{}]", oldSeatStations);
		String[] oldSeatStationsArray = oldSeatStations.split(",");
		StringBuffer newSeatStations = new StringBuffer();
		boolean flag = false;
		for (int i = 0; i < oldSeatStationsArray.length; i++) {
			String oldSeatStation = oldSeatStationsArray[i];
			log.info("#{},#{},#{}-{}", oldSeatStation.indexOf(ticketPurchaseVo.getStartStation()),
					oldSeatStation.lastIndexOf(ticketPurchaseVo.getEndStation()), oldSeatStation.length(),
					ticketPurchaseVo.getEndStation().length());
			if (oldSeatStation.indexOf(ticketPurchaseVo.getStartStation()) == 0
					&& oldSeatStation.lastIndexOf(ticketPurchaseVo.getEndStation()) == oldSeatStation.length()
							- ticketPurchaseVo.getEndStation().length()) {
				flag = true;
			} else {
				newSeatStations.append(oldSeatStation).append(",");
			}
		}
		if (flag) {
			String newSeatStationsStr = "";
			if (oldSeatStationsArray.length == 1) {
				newSeatStationsStr = newSeatStations + "";
			} else {
				newSeatStationsStr = newSeatStations.substring(0, newSeatStations.length() - 1);
			}
			RedisPool.set(seatKey, newSeatStationsStr);
			log.info("#退票成功,#oldSeatStations:[{}];#newSeatStations:[{}]", oldSeatStations, newSeatStations);
			return RespUtil.dataResp(newSeatStations);
		} else {
			log.info("#退票失败,#oldSeatStations:[{}];#newSeatStations:[{}]", oldSeatStations, newSeatStations);
			throw new ServiceException(RespCode.ticket_repeatRefund);
		}
	}

	@RequestMapping("/remainticket")
	Object remainTicket(@RequestBody TicketRemainVo ticketRemainVo) {
		if (StringUtils.isEmpty(ticketRemainVo.getTrainId())) {
			throw new ServiceException(RespCode.PARAM_INCOMPLETE, "TrainId");
		}
		if (StringUtils.isEmpty(ticketRemainVo.getStartStation())) {
			throw new ServiceException(RespCode.PARAM_INCOMPLETE, "StartStation");
		}
		if (StringUtils.isEmpty(ticketRemainVo.getEndStation())) {
			throw new ServiceException(RespCode.PARAM_INCOMPLETE, "EndStation");
		}
		String ticketInfoStr = RedisPool.get(RedisCons.trainInfoKey + ticketRemainVo.getTrainId());
		// 获取列车座位list信息
		String seatListStr = JSONObject.parseObject(ticketInfoStr).get("seatList") + "";
		// 遍历已经购票的所有座位，是否和当前查询行程冲突，（当前行程余票数=列车座位数-已购座位冲突数）
		String[] seatListArray = seatListStr.split(",");
		int purchasedCount = 0;
		for (String seat : seatListArray) {
			String key = RedisCons.ticketPurchaseList + ticketRemainVo.getTrainId() + "-" + seat;
			if (RedisPool.exists(key)) {
				String seatStation = RedisPool.get(key);
				if (seatStation.contains(ticketRemainVo.getStartStation())
						|| seatStation.contains(ticketRemainVo.getEndStation())) {
					purchasedCount++;
				}
			}
		}
		log.info("#seatListArray.length:[{}],#purchasedCount:[{}]", seatListArray.length, purchasedCount);
		ticketRemainVo.setRemainCount(seatListArray.length - purchasedCount);
		return ticketRemainVo;
	}

	@RequestMapping("/stopstation")
	Object stopStation(@RequestBody TicketInitVo ticketInitVo) {
		String ticketInfoStr = RedisPool.get(RedisCons.trainInfoKey + ticketInitVo.getTrainId());
		return RespUtil.dataResp(JSONObject.parseObject(ticketInfoStr).get("stopStation"));
	}

	@RequestMapping("/seatlist")
	Object seatList(@RequestBody TicketInitVo ticketInitVo) {
		String ticketInfoStr = RedisPool.get(RedisCons.trainInfoKey + ticketInitVo.getTrainId());
		return RespUtil.dataResp(JSONObject.parseObject(ticketInfoStr).get("seatList"));
	}

}